package gamebackend;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import framework.Color;
import framework.GraphicsWrapper;
import framework.ImageTinter;
import framework.ImageWrapper;
import framework.KPool;
import framework.KPool.PoolObjectFactory;
import framework.ResourcesWrapper;
import gamebackend.Ship.ShipListener;

public class LaserShip extends AbstractShip {
	
	private static ImageWrapper my_image;
	public static void initialize(ResourcesWrapper the_resources) {
		my_image = the_resources.loadImage("ship_lasership.png");
	}
	
	// laser stuff, unique to laserShip.
	private static final double LASER_SPEED = 210 / 1000.0;
	private static final int LASER_MIN_TIME = 200;
	private static final int LASER_INTERVAL = 1000 / 30;
	private static final int LASER_MAX_POWER = 1400;
	private static final double LASER_RECHARGE = .15;
	/**
	 * Laser recharge rate penalty timer for after the laser is shot.  This
	 * way shooting a lot of times in sequence recharges slower than
	 * shooting less frequently.
	 */
	private static final int LASER_RECHARGE_PENATLY_TIME = 4000;
	
	private boolean my_laserIsOn;
	private int my_laserMinTimer;
	private int my_laserIntervalTimer;
	private int my_timeSinceLastLaser;
	/**
	 * How much laser is left, out of LASER_MAX_POWER
	 */
	private double my_laserPower;
	/**
	 * Laser recharge rate penalty timer for after the laser is shot.  This
	 * way shooting a lot of times in sequence recharges slower than
	 * shooting less frequently.
	 */
	private int my_laserRechargePenaltyTime;
	private List<LaserBeam> my_lasers;
	private final int[] my_rewards = new int[TIERS];
	private final BlockField my_blockField;
	
	public LaserShip(final ShipListener the_listener, final BlockField the_blockField, final double the_x, double the_y) {
		super(the_listener, the_x, the_y);
		my_laserPower = LASER_MAX_POWER;
		my_lasers = new ArrayList<LaserBeam>();
		my_blockField = the_blockField;
	}
	//TODO remove this?
	private LaserShip(Ship the_ship) {
		super(the_ship);
		my_lasers = new ArrayList<LaserBeam>();
		my_blockField = null;
	}
	public LaserShip(final double the_x, final double the_y) {
		this(null, null, the_x, the_y);
	}
	public Ship getCopy() {
		return new LaserShip(this);
	}
	@Override
	public void step(int timeDelta, double cameraYChange) {
		super.step(timeDelta, cameraYChange);
		laserStep(timeDelta, cameraYChange);
	}
	private void laserStep(int timeDelta, double cameraYChange) {
		if (my_laserIntervalTimer > 0) {
			my_laserIntervalTimer -= timeDelta;
		}
		if (my_laserRechargePenaltyTime > 0) {
			my_laserRechargePenaltyTime -= timeDelta;
			if (my_laserRechargePenaltyTime < 0) {
				my_laserRechargePenaltyTime = 0;
			}
		}
		
		if ((my_laserPower > LASER_MIN_TIME && my_laserIsOn) || my_laserMinTimer > 0) {
			if (my_laserMinTimer <= 0) {
				my_laserMinTimer = LASER_MIN_TIME;
			}
			my_timeSinceLastLaser += timeDelta;
			my_laserMinTimer -= timeDelta;
			while (my_laserPower > 0 && my_laserIntervalTimer <= 0) {
				my_laserIntervalTimer += LASER_INTERVAL;
				if (!isAlive()) {
					return;
				}
				int tier = my_rewards.length - 1;
				while (tier > 0 && my_rewards[tier] == 0) {
					tier--;
				}
				if (tier > 0) {
					my_rewards[tier]--;
					final int radius = tier - 1;
					for (int y = 0; y < 1 + 1; y++) {
						for (int x = -radius; x <= radius; x++) {
							my_lasers.add(new LaserBeam(20,
									getXPosition(),
									-5 + 5 * Math.abs(x) + getYPosition() + y * 7, 
									x * .65*1.0 / 150 * (1 + .2 * y),
									getYVelocity() + LASER_SPEED * (.8 + .2 * tier),
									20));
						}
					}
				} else {
					my_lasers.add(new LaserBeam(20,
							getXPosition(), getYPosition(),
							0, getYVelocity() + LASER_SPEED, my_timeSinceLastLaser));
					my_laserPower -= LASER_INTERVAL;
					
					my_laserRechargePenaltyTime = LASER_RECHARGE_PENATLY_TIME;
				}
				my_timeSinceLastLaser = 0;
			}
		} else {
			my_laserPower += timeDelta * LASER_RECHARGE * (1 - (double)my_laserRechargePenaltyTime / LASER_RECHARGE_PENATLY_TIME);
			if (my_laserPower > LASER_MAX_POWER) {
				my_laserPower = LASER_MAX_POWER;
			}
		}
		for (Iterator<LaserBeam> it = my_lasers.iterator();it.hasNext();) {
			LaserBeam laser = it.next();
			laser.step(timeDelta, cameraYChange);
			int hits = my_blockField.checkRectangleAndDestroy(laser.getLeft(), laser.getTop(),
						laser.getWidth(), laser.getHeight());
				
			laser.collide(hits);
				
			if (!laser.isAlive()) {
				it.remove();
			}
		}
	}
	public void special(final boolean is_on) {
		toggleLaser(is_on);
	}
	private void toggleLaser(boolean pressed) {
		if (!my_laserIsOn && pressed && my_laserPower > LASER_MIN_TIME) {
			my_laserMinTimer = LASER_MIN_TIME;
		}
		my_laserIsOn = pressed;
		
		
	}
	
	public int getSpecialSegmentCount() {
		return LASER_MAX_POWER / LASER_MIN_TIME;
		
	}
	public double getSpecialPercent() {
		return 100 * my_laserPower / LASER_MAX_POWER;
	}
	
	@Override
	public void boost() {
		super.boost();
		
	}
	@Override
	protected void onNewTier(int the_tier) {
		if (my_blockField == null) {
			return;
		}
//		for (int i = 1; i <= the_tier; i++) {
//			my_rewards[the_tier] += 5;
//		}
		my_laserPower += Math.min(LASER_MAX_POWER - my_laserPower, LASER_MAX_POWER / 5);
		final List<LaserBeam> bonus;
		bonus = new ArrayList<LaserBeam>();
		if (the_tier < 2) {
			//my_yVelo += .5 * BOOST_SPEED;
		} else {
			final int radius = the_tier - 1;
			for (int y = 0; y < 1 + 1 * the_tier; y++) {
				for (int x = -radius; x <= radius; x++) {
					bonus.add(new LaserBeam(20,
							getXPosition(),
							-5 + 5 * Math.abs(x) + getYPosition() + y * 7, 
							x * .65*1.0 / 150 * (1 + .2 * y),
							getYVelocity() + LASER_SPEED,
							20));
				}
			}
		}
		my_lasers.addAll(bonus);
	}
	
	
	@Override
	public void render(GraphicsWrapper g) {
		for (LaserBeam laser : my_lasers) {
			laser.render(g);
		}
		for (LaserBeam laser : my_lasers) {
			laser.postRender(g);
		}
		super.render(g);
	}
	
	/**
	 * Able to destroy blocks. This is so the LaserShip can manage Lasers itself
	 * while still destroying blocks.
	 * 
	 * @author Jonathan Caddey
	 */
	// this could perhaps be made better by passing the ship Steppables or Renderables or whatever interface
	// that could be made and some way of saying what kind of collisions it cares about and what
	// to do when colliding.  Or perhaps have the BlockField a class itself, seperate from Gamesession,
	// so Ship manages collisions, not GameSession.  Actually I like that, in case I have a ghosting thing.
	public interface BlockField {
		public int checkRectangleAndDestroy(double the_x, double the_y, double the_width, double the_height);
	}

	@Override
	protected ImageWrapper getImage() {
		return my_image;
	}
	@Override
	protected double getSidewaysSpeed() {
		return 0.21500082851239669;
	}
	@Override
	protected double getSidewaysAcceleration() {
		return 7.114723493386153E-4;
	}
	@Override
	public int getMaxHealth() {
		return 4;
	}
}
